/**
 * All Rights Reserved , Copyright (C) 2020 , 青岛鼎信通讯股份有限公司
 * BasePropertyService
 * 基础字段信息
 * 修改纪录
 * 2020-12-01 版本：1.0 zhanglongfei 创建。
 *
 * @version 版本：1.0
 * @author 作者：zhanglongfei
 * 创建日期：2020-12-01
 */
package com.topscomm.base.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.topscomm.base.BaseSystemConst;
import com.topscomm.base.service.IBasePropertyService;
import com.topscomm.base.service.impl.auto.BasePropertyServiceAuto;
import com.topscomm.basic.BasicService;
import com.topscomm.cbo.pojo.CboAttachmentEntity;
import com.topscomm.pub.util.ConvertUtil;
import com.topscomm.pub.util.ObjectWithMapUtil;
import com.topscomm.pub.util.SpringUtil;
import com.topscomm.pub.util.StringBufferProxy;
import com.topscomm.pub.util.StringUtil;

@Service(value = "basePropertyService")
@Scope(value = "singleton")
public class BasePropertyService extends BasePropertyServiceAuto implements IBasePropertyService {
    @Autowired
    private RedisTemplate redisTemplate;
    // 在baseproperty中使用属性的范围
    private static final String BASEPROPERTYSCOPE = "calculate";

    /**
     * @param customerCode //客户code
     * @param sourceType   //来源表名
     * @return java.math.BigDecimal
     * @description: 计算客户完整度
     * @author: wangxinhua
     * @date: 2020/12/7 11:23
     * @modify:
     */
    @Override
    public BigDecimal calculateCompleteness(String customerCode, String sourceType) throws Exception {
        List<Map<String, Object>> baseProperties = this.getBaseProperty(sourceType, BASEPROPERTYSCOPE);
        int total = baseProperties.size();
        int finish = 0;
        if (total == 0) {
            BigDecimal completeness = new BigDecimal("0.00");
            this.updateCalculateCompleteness(customerCode, completeness, sourceType);
            return completeness;
        }

        //获取list类型baseproperty完整度数据
        int[] listTypeRes = this.listTypeCalculate(baseProperties, customerCode, sourceType);
        total += listTypeRes[0];
        finish += listTypeRes[1];

        //获取form类型baseproperty完整度数据
        List<Map<String, Object>> formTypeProperties = baseProperties.stream()
                .filter(item -> BaseSystemConst.BasePropertyDataTypeEnum.Form.equals(ConvertUtil.convertToString(item.get("datatype"))))
                .collect(Collectors.toList());
        int[] formTypeRes = this.formTypeCalculate(formTypeProperties, customerCode);
        total += formTypeRes[0];
        finish += formTypeRes[1];

        BigDecimal finishBD = new BigDecimal(finish);
        BigDecimal totalBD = new BigDecimal(total);
        BigDecimal hundred = new BigDecimal("100");
        BigDecimal completeness = (finishBD.divide(totalBD, 4, RoundingMode.HALF_UP).multiply(hundred)).setScale(2, RoundingMode.HALF_UP);
        this.updateCalculateCompleteness(customerCode, completeness, sourceType);
        return completeness;
    }

    /**
     * @param baseProperties
     * @param customerCode
     * @param sourceType
     * @return int[]  int[0] //总个数  int[1] //完成个数
     * @description: 计算list类型的baseproperty的完整度
     * @author: wangxinhua
     * @date: 2020/12/19 11:08
     * @modify:
     */
    private int[] listTypeCalculate(List<Map<String, Object>> baseProperties, String customerCode, String sourceType) {
        int[] calculate = new int[2];
        calculate[0] = 0;  //总个数
        calculate[1] = 0;  //完成个数

        //list类型维护的表单c
        List<String> listTypeProperties = new ArrayList<String>();
        //存储拼接sql要用的数据
        List<String[]> listTypeRelationProperties = new ArrayList<>();
        for (Map<String, Object> p : baseProperties) {
            if (BaseSystemConst.BasePropertyDataTypeEnum.List.equals(p.get("datatype"))) {
                String[] tableInfo = {
                        ConvertUtil.convertToString(p.get("relationsource")),
                        ConvertUtil.convertToString(p.get("code")),
                        ConvertUtil.convertToString(p.get("relationkey").toString()),
                        ConvertUtil.convertToString(p.get("relationkind"))
                };
                listTypeProperties.add(p.get("code").toString());
                listTypeRelationProperties.add(tableInfo);
            }
        }
        //根据子表的分类(relationkind)进行查询数量的字段
        List<String> kindSearchNumKeys = new ArrayList<String>();
        // 拼接查询sql
        StringBufferProxy dataCodeSql = new StringBufferProxy();
        dataCodeSql.appendSingle("SELECT {0}.* ", sourceType);
        for (String[] relation : listTypeRelationProperties) {
            //获取relationkind
            org.json.JSONArray kindJsonArray = new org.json.JSONArray();
            if (!StringUtil.isEmpty(relation[3])) {
                kindJsonArray = new org.json.JSONArray(relation[3]);
            }
            //关联子表时,用主表的某个字段关联
            String primarySearchKey = "code";
            //判断是否附件
            if (CboAttachmentEntity.tableName.equals(relation[0])) {
                //附件用id关联子表
                primarySearchKey = "id";
            }
            if (kindJsonArray.length() > 0) {
                //total增加种类的个数-1(减去list的那条数据)
                calculate[0] = calculate[0] + kindJsonArray.length() - 1;
                //循环子表的类别,拼接sql
                for (int i = 0; i < kindJsonArray.length(); i++) {
                    org.json.JSONObject jsonObject = kindJsonArray.getJSONObject(i);
                    dataCodeSql.appendSingle(",( SELECT COUNT( * ) FROM {0} where {3} = {1}.{4} {5} ) AS {2} ",
                            relation[0],
                            sourceType,
                            relation[1] + jsonObject.get("kindvalue") + "num",
                            relation[2],
                            primarySearchKey,
                            "and " + jsonObject.get("kindkey") + "='" + jsonObject.get("kindvalue") + "'");
                    kindSearchNumKeys.add(relation[1] + ConvertUtil.convertToString(jsonObject.get("kindvalue")).toLowerCase() + "num");
                }
            } else {
                dataCodeSql.appendSingle(",( SELECT COUNT( * ) FROM {0} where {3} = {1}.{4} ) AS {2} ",
                        relation[0], sourceType, relation[1] + "num", relation[2], primarySearchKey);
            }
        }
        dataCodeSql.appendSingle("FROM {0} WHERE {1}.code = '{2}'", sourceType, sourceType, customerCode);
        //进行查询
        List<Map<String, Object>> sourceTypeNameList = this.selectExecute(dataCodeSql.toString());
        //获取客户数据
        Map<String, Object> customerInfo = sourceTypeNameList.get(0);
        for (Map<String, Object> bp : baseProperties) {
            if (listTypeProperties.contains(bp.get("code").toString())) {
            	//list属性的
                String key = bp.get("code").toString().toLowerCase() + "num";
                if (customerInfo.containsKey(key)) {
                    BigDecimal listNum = new BigDecimal(customerInfo.get(key).toString());
                    if (listNum.intValue() > 0) {
                        calculate[1]++;
                    }
                }
            } else {
                String key = bp.get("code").toString();
                if (customerInfo.containsKey(key)
                        && customerInfo.get(key) != null
                        && !StringUtil.isEmpty(customerInfo.get(key).toString())) {
                    calculate[1]++;
                }
            }
        }
        //获取根据子表分类查询的完整度
        for (String key : kindSearchNumKeys) {
            if (customerInfo.containsKey(key)) {
                BigDecimal listNum = new BigDecimal(customerInfo.get(key).toString());
                if (listNum.intValue() > 0) {
                    calculate[1]++;
                }
            }
        }

        return calculate;
    }

    /**
     * @param formTypeProperties
     * @param customerCode
     * @return int[]  int[0] //总个数  int[1] //完成个数
     * @description: 计算form类型的baseproperty的完整度
     * @author: wangxinhua
     * @date: 2020/12/19 11:09
     * @modify:
     */
    private int[] formTypeCalculate(List<Map<String, Object>> formTypeProperties, String customerCode) throws Exception {
        int[] calculate = new int[2];
        calculate[0] = 0;  //总个数
        calculate[1] = 0;  //完成个数

        for (Map<String, Object> formProp : formTypeProperties) {
            //获取当前form表的表名
            String relationSource = ConvertUtil.convertToString(formProp.get("relationsource"));
            //获取当前表的可计算完整度的属性
            List<Map<String, Object>> formBaseProps = this.getBaseProperty(relationSource, BASEPROPERTYSCOPE);
            //可计算属性的总个数
            int calculateProps = formBaseProps.size();
            //添加需要计算完整度的总数(-1是减去总数的form那条数据)
            calculate[0] = calculate[0] + calculateProps - 1;
            //获取当前表的service
            BasicService basicService;
            String serviceName = relationSource.substring(0, 1).toLowerCase()
                    + relationSource.substring(1)
                    + "Service";
            basicService = (BasicService) SpringUtil.getBean(serviceName);
            //获取表中数据
            List<Map<String, Object>> dataList = basicService.queryMapByWhere(" customercode = '" + customerCode + "'");
            //如果存在子表数据
            if (dataList.size() > 0) {
                //确定要计算的数据
                Map<String, Object> formData = dataList.get(0);
                for (Map<String, Object> bp : formBaseProps) {
                    //获取要计算的key
                    String key = bp.get("code").toString();
                    //如果不为空+1
                    if (formData.containsKey(key)
                            && formData.get(key) != null
                            && !StringUtil.isEmpty(formData.get(key).toString())) {
                        calculate[1]++;
                    }
                }
            }
        }
        return calculate;
    }

    /**
     * @param customerCode
     * @param completeness
     * @param sourceType
     * @return void
     * @description: 更新完整度
     * @author: wangxinhua
     * @date: 2020/12/19 11:16
     * @modify:
     */
    private void updateCalculateCompleteness(String customerCode, BigDecimal completeness, String sourceType) {
        Map<String, Object> updateMap = new HashMap<String, Object>();
        updateMap.put("completeness", completeness);
        BasicService<?, ?> baseService;
        String serviceName = sourceType.substring(0, 1).toLowerCase()
                + sourceType.substring(1)
                + "Service";
        baseService = (BasicService<?, ?>) SpringUtil.getBean(serviceName);
        baseService.updateByWhere(updateMap, " code = '" + customerCode + "'");
    }

    /**
     * @param sourcetype
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @description: 获取可比较的属性值
     * @author: wangxinhua
     * @date: 2020/12/2 13:53
     * @modify:
     */
    @Override
    public List<Map<String, Object>> getBaseProperty(String sourcetype, String basePropertyScope) throws Exception {
        List<Map<String, Object>> baseProperties = new ArrayList<Map<String, Object>>();
        // 定义redis的key
        String key = "mcr:baseproperty:" + sourcetype;
        boolean hasKey = this.redisTemplate.hasKey(key);
        if (hasKey) {
            // 缓存存在
            baseProperties = this.redisTemplate.opsForList().range(key, 0, -1);
        } else {
            // 缓存不存在
            // 获取可修改的基础数据
            if(sourcetype.equals("AllProperty")){
                baseProperties = this.queryMapByWhere(" 1 = 1 ");
            }else{
                StringBufferProxy sqlBufferProxy = new StringBufferProxy();
                sqlBufferProxy.appendLineSingle("sourcetype='{0}' AND enabled = 1 ", sourcetype);
                baseProperties = this.queryMapByWhere(sqlBufferProxy.toString());
            }
            try {
                this.redisTemplate.opsForList().rightPushAll(key, baseProperties);
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        Iterator<Map<String, Object>> it = baseProperties.iterator();
        while (it.hasNext()) {
            Map<String, Object> obj = ObjectWithMapUtil.ObjectToMap(it.next());
            if (obj.get("scope") != null) {
                String[] scopeList = obj.get("scope").toString().split(",");
                List<String> list = Arrays.asList(scopeList);
                boolean result = list.contains(basePropertyScope);
                if (!result) {
                    it.remove();
                }
            } else {
                it.remove();
            }
        }
        return baseProperties;
    }
}
